/*
Copyright © 2024 weidongkl <weidongkx@gmail.com>
*/

package hamming

type Hamming struct {
}

func New() *Hamming {
	return &Hamming{}
}

func (c *Hamming) Calculate(s1, s2 string) (float64, error) {
	return HammingSimilarity(s1, s2)
}

func (c *Hamming) Name() string {
	return "hamming"
}

// HammingDistance calculates the Hamming distance between two strings.
// s1 and s2 are the input strings to compare.
// The function returns the Hamming distance between the two strings, and an error if any.
// If the lengths of the two strings are not equal,
// the function returns the current Hamming distance calculation along with an error.
func HammingDistance(s1, s2 string) (int, error) {

	var (
		// lenS1 and lenS2 store the lengths of strings s1 and s2 respectively.
		lenS1 = len(s1)
		lenS2 = len(s2)
		// distance accumulates the Hamming distance.
		distance = 0
	)
	// Checks if the lengths of the two strings are equal.
	if lenS1 != lenS2 {
		// Returns the current Hamming distance and an error object if lengths are unequal.
		return distance, HammingDistanceError
	}
	// Iterates through the strings, comparing characters at each position.
	for i := 0; i < lenS1; i++ {
		// Increments distance if characters at the current position do not match.
		if s1[i] != s2[i] {
			distance++
		}
	}
	// If the lengths of the strings are equal, returns the computed Hamming distance with a nil error.
	return distance, nil
}

// HammingSimilarity calculates the Hamming similarity between two strings.
// s1 and s2 are the two strings to compare.
// The return value is the Hamming similarity of the two strings, ranging from 0 to 1, where 1 indicates they are identical.
// If an error occurs during the calculation, it returns the corresponding error message.
func HammingSimilarity(s1, s2 string) (float64, error) {
	// Calculate the Hamming distance between the two strings.
	distance, err := HammingDistance(s1, s2)
	// Check if there was an error in calculating the Hamming distance. If so, return 0 and the error.
	if err != nil {
		return 0, err
	}
	// Get the length of string s1.
	lenS1 := len(s1)
	// If the length of s1 is 0, it means the two strings are identical. Return 1 for maximum similarity.
	if lenS1 == 0 {
		return 1, nil
	}
	// Calculate the Hamming similarity, which is the difference in lengths divided by the total length.
	// The returned value is a floating-point number representing the similarity of the two strings.
	return float64(lenS1-distance) / float64(lenS1), nil
}
